#include <time.h>
#include <stdarg.h>
#include <string>
#include <sstream>

#include "GLog.h"
#include "GError.h"

#if defined G_BUILD_DEBUG
	#if defined (_MSC_VER)
		#define WIN32_LEAN_AND_MEAN
		#include <Windows.h>

		#define	GCompilerOutput(cString)		OutputDebugString(cString)
	#elif defined (__APPLE__)
		#define	GCompilerOutput(cString)
	#elif defined (__MINGW32__)
		#define	GCompilerOutput(cString)
	#elif defined (__GNUC__) && defined (__unix__)
		#define	GCompilerOutput(cString)
	#elif defined (__GNUC__) && defined (_WIN32)
		#define	GCompilerOutput(cString)
	#else
		#define	GCompilerOutput(cString)
	#endif
#else
	#define	GCompilerOutput(cString)
#endif


namespace GUtil
{

	/********************************************************/
	/*						GFileLog						*/
	/********************************************************/

	// Description: Constructor
	// Return:
	// Notes:
	// -----------------------------------------------------
	GFileLog::GFileLog()
	{

	}

	// Description: Destructor
	// Return:
	// Notes:
	// -----------------------------------------------------
	GFileLog::~GFileLog()
	{
		Close();
	}

	// Description:
	// Return:
	// Notes:
	// -----------------------------------------------------
	GSize GFileLog::GetSize() const
	{
		return 0;
	}

	// Description: Tries to open the file stream.
	// Return: True is successful, false otherwise.
	// Notes: Is stream is already open, stream is closed and then opened with given file name.
	// -----------------------------------------------------
	bool GFileLog::Open(const char* FileName)
	{
		if ( FileStream.is_open() )
			FileStream.close();
	
		FileStream.open( FileName, std::ofstream::out | std::ofstream::trunc );
		if ( !FileStream.is_open() )
		{
			GError::Error( GErrorMessage( "Cannot open file stream." ) );
			return false;
		}

		return true;
	}

	// Description: Closes the file stream
	// Return: 
	// Notes:
	// -----------------------------------------------------
	void GFileLog::Close()
	{
		if (!FileStream.is_open())
		{
			return;
		}

		FileStream.flush();
		FileStream.close();
	}

	void GFileLog::Flush()
	{
		if (!FileStream.is_open())
		{
			return;
		}

		FileStream.flush();
	}

	// Description: Writes the given string to file stream
	// Return: 
	// Notes:
	// -----------------------------------------------------
	void GFileLog::Write(const char* String)
	{
		GError::DebugCheck( !FileStream.is_open(), "File stream is not open." );

		FileStream << String;
	}

	// Description: Moves to new line
	// Return: 
	// Notes:
	// -----------------------------------------------------
	void GFileLog::NewLine()
	{
		GError::DebugCheck( !FileStream.is_open(), "File stream is not open." );

		FileStream << std::endl;
	}

	/********************************************************/
	/*						GFileLogger						*/
	/********************************************************/
	
	// Description: Constructor
	// Return: 
	// Notes:
	// -----------------------------------------------------
	GFileLogger::GFileLogger( )
	{
		LineNumber = 0;

		FileLog.Open(G_ENGINE_LOG_FILE);
	}

	// Description: Destructor
	// Return: 
	// Notes:
	// -----------------------------------------------------
	GFileLogger::~GFileLogger( )
	{
		FileLog.Close();
	}

	// Description: Changes log file path to a user specified one
	// Return: Retuns true if successfull, false otherwise
	// Notes: Opens the default log file if function fails, content is erased
	// -----------------------------------------------------
	bool GFileLogger::SetLogFile( const char* FileName )
	{
		FileLog.Close();
	
		bool Result = FileLog.Open(FileName);
		if(!Result)
		{
			GError::Error( GErrorMessage( "Can not open specified log file." ) );

			FileLog.Open(G_ENGINE_LOG_FILE);
			return false;
		}

		return true;
	}

	void GFileLogger::Flush()
	{
		FileLog.Flush();
	}

	// Description: Logs the given string to the file.
	// Return: 
	// Notes:
	// -----------------------------------------------------
	void GFileLogger::Log( const char* LogType, const char* Format, ... )
	{
		va_list Arguments;
		va_start(Arguments, Format);
	
		Log(LogType, Format, Arguments);

		va_end(Arguments);
	}

	void GFileLogger::Log( const char* LogType, const char* Format, va_list Arguments )
	{
		char TimeBuffer[32];
		char StringBuffer[512];
	
		std::stringstream LogHeader;

		vsprintf_s( StringBuffer, 512, Format, Arguments );

		LogHeader.fill('0');
		LogHeader.width(10);

		LogHeader << LineNumber++ << "-";

		time_t Time = time( NULL );
		ctime_s( TimeBuffer, 32, &Time );
		TimeBuffer[24] = '\0';

		LogHeader << "[ " << TimeBuffer << " | " << clock() << " ]";
	
		LogHeader << " <" << LogType << "> : ";

		FileLog.Write(LogHeader.str().c_str());
		FileLog.Write(StringBuffer);
		FileLog.NewLine();

		GCompilerOutput(StringBuffer);
		GCompilerOutput("\n");
	}

	// Description: Static method to access singleton instance
	// Return: Returns the only instance
	// Notes:
	// -----------------------------------------------------
	GFileLogger* GFileLogger::GetInstance()
	{
		static GFileLogger Logger;
		return &Logger;
	}

}; // namespace GUtil